using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace NSimpleDB.Service.VistaDb.QueryProcessing.Compiler
{
    internal enum QueryTokensTypes
    {
        IntersectionOp,
        UnionOp,

        OpenPredicate,
        ClosePredicate,
        
        AttributeNameOrValue,

        NotOp, // for item sets or attribute comparisons
        AndOp,
        OrOp,

        EqualOp,
        NotEqualOp,
        LargerOp,
        LargerOrEqualOp,
        LessOp,
        LessOrEqualOp,
        StartsWithOp,

        ERROR,
        EOQ  // end of query
    }


    internal class QueryTokenizer
    {
        private MatchCollection queryTokenMatches;
        private int iCurrTokenMatch;

        private QueryTokensTypes currentTokenType;
        private string currentTokenText;
        private int currentTokenPosition;


        public QueryTokenizer(string query)
        {
            Regex reQuery = new Regex(@"(?<lookItUp>intersection|union|\[|\]|not|and|or|=|!=|>=|>|<=|<|starts-with)|(?<attributeNameOrValue>''|'([^']|'')+')|(?<whitespace>\s+)|(?<error>.)", RegexOptions.IgnoreCase);
            this.queryTokenMatches = reQuery.Matches(query);
            this.iCurrTokenMatch = -1;

            this.currentTokenType = QueryTokensTypes.EOQ;
            this.currentTokenText = "";
            this.currentTokenPosition = -1;
        }


        public QueryTokensTypes Read()
        {
            if (this.iCurrTokenMatch < this.queryTokenMatches.Count)
            {
                // find next non-whitespace token
                do
                {
                    this.iCurrTokenMatch++;
                } while(this.iCurrTokenMatch < this.queryTokenMatches.Count &&
                        this.queryTokenMatches[this.iCurrTokenMatch].Groups["whitespace"].Success);

                // determine token type - or end-of-query reached
                if (this.iCurrTokenMatch < this.queryTokenMatches.Count)
                {
                    Match currentTokenMatch = this.queryTokenMatches[this.iCurrTokenMatch];
                    this.currentTokenType = DetermineCurrentTokenType(currentTokenMatch);
                    this.currentTokenText = currentTokenMatch.Value;
                    this.currentTokenPosition = currentTokenMatch.Index;
                    return this.currentTokenType;
                }
                else
                {
                    this.currentTokenType = QueryTokensTypes.EOQ;
                    this.currentTokenText = "";
                    this.currentTokenPosition = -1;
                    return this.currentTokenType;
                }
            }
            else
            {
                this.currentTokenType = QueryTokensTypes.EOQ;
                this.currentTokenText = "";
                this.currentTokenPosition = -1;
                return this.currentTokenType;
            }
        }

        private QueryTokensTypes DetermineCurrentTokenType(Match queryTokenMatch)
        {
            if (queryTokenMatch.Groups["lookItUp"].Success)
            {
                switch (queryTokenMatch.Value.ToLower())
                {
                    case "intersection": return QueryTokensTypes.IntersectionOp;
                    case "union": return QueryTokensTypes.UnionOp;
                    case "[": return QueryTokensTypes.OpenPredicate;
                    case "]": return QueryTokensTypes.ClosePredicate;
                    case "not": return QueryTokensTypes.NotOp;
                    case "and": return QueryTokensTypes.AndOp;
                    case "or": return QueryTokensTypes.OrOp;
                    case "=": return QueryTokensTypes.EqualOp;
                    case "!=": return QueryTokensTypes.NotEqualOp;
                    case ">": return QueryTokensTypes.LargerOp;
                    case ">=": return QueryTokensTypes.LargerOrEqualOp;
                    case "<": return QueryTokensTypes.LessOp;
                    case "<=": return QueryTokensTypes.LessOrEqualOp;
                    case "starts-with": return QueryTokensTypes.StartsWithOp;
                    default: return QueryTokensTypes.ERROR;
                }
            }
            else if (queryTokenMatch.Groups["attributeNameOrValue"].Success)
                return QueryTokensTypes.AttributeNameOrValue;
            else
                return QueryTokensTypes.ERROR;
        }


        public QueryTokensTypes CurrentTokenType
        {
            get
            {
                return this.currentTokenType;
            }
        }


        public string CurrentTokenText
        {
            get
            {
                return this.currentTokenText;
            }
        }

        public int CurrentTokenPosition
        {
            get
            {
                return this.currentTokenPosition;
            }
        }
    }
}
